//
//  UIImage+FWPowerful.m
//  iOSToolkit
//
//  Created by Evan Xie on 9/17/14.
//  Copyright (c) 2014 Family Wings. All rights reserved.
//

#import "UIImage+FWPowerful.h"


/* 黑白: Black white */
const float kCMTBlackWhite[] = {
    0.3086f, 0.6094f, 0.0820f, 0, 0,
    0.3086f, 0.6094f, 0.0820f, 0, 0,
    0.3086f, 0.6094f, 0.0820f, 0, 0,
    0, 0, 0, 1, 0
};

/* 颜色反相: Color revert */
const float kCMTColorRevert[] = {
    -1,  0,  0, 0, 255.0f,
    0, -1,  0, 0, 255.0f,
    0,  0, -1, 0, 255.0f,
    0,  0,  0, 1, 0
};

/*哥特式: Gothic, 红与黑*/
const float kCMTGothic[] = {
    1.9f, -0.3f, -0.2f, 0, -87.0f,
    -0.2f,  1.7f, -0.1f, 0, -87.0f,
    -0.1f, -0.6f,  2.0f, 0, -87.0f,
    0, 0, 0, 1, 0
};

/* 锐化: Sharpen */
const float kCMTSharpen[] = {
    4.8f,-1.0f, -0.1f, 0,-388.4f,
    -0.5f,4.4f, -0.1f, 0,-388.4f,
    -0.5f,-1.0f, 5.2f, 0,-388.4f,
    0, 0, 0, 1.0f, 0
};

/* 淡雅: Simple but elegant */
const float kCMTSimpleElegant[] = {
    0.6f, 0.3f, 0.1f, 0, 73.3f,
    0.2f, 0.7f, 0.1f, 0, 73.3f,
    0.2f, 0.3f, 0.4f, 0, 73.3f,
    0, 0, 0, 1, 0
};

/* 酒红: Wine red */
const float kCMTWineRed[] = {
    1.2f, 0.0f, 0.0f, 0, 0,
    0.0f, 0.9f, 0.0f, 0, 0,
    0.0f, 0.0f, 0.8f, 0, 0,
    0, 0, 0, 1, 0
};

/* 梦幻: Dream */
const float kCMTDream[] = {
    0.8f, 0.3f, 0.1f, 0.0f, 46.5f,
    0.1f, 0.9f, 0.0f, 0.0f, 46.5f,
    0.1f, 0.3f, 0.7f, 0.0f, 46.5f,
    0.0f, 0.0f, 0.0f, 1.0f, 0.0f
};

/* 夜色: Night */
const float kCMTNight[] = {
    1.0f, 0.0f, 0.0f, 0.0f, -66.6f,
    0.0f, 1.1f, 0.0f, 0.0f, -66.6f,
    0.0f, 0.0f, 1.0f, 0.0f, -66.6f,
    0.0f, 0.0f, 0.0f, 1.0f, 0.0f
};


@implementation UIImage (FWPowerful)

- (UIImage *)imageWithTintColor:(UIColor *)tintColor
{
    if (!tintColor) {
        return self;
    }
    
    UIGraphicsBeginImageContext(self.size);
    CGRect drawRect = CGRectMake(0, 0, self.size.width, self.size.height);
    [self drawInRect:drawRect];
    [tintColor set];
    UIRectFillUsingBlendMode(drawRect, kCGBlendModeSourceAtop);
    UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return tintedImage;
}


- (UIImage *)imageWithLinearGradientTintColors:(NSArray *)gradientColors
{
    if (gradientColors.count == 0) {
        return self;
    }
    
    UIGraphicsBeginImageContext(CGSizeMake(self.size.width, self.size.height));
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(context, 0, self.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    CGContextDrawImage(context, rect, self.CGImage);
    
    // Create gradient
    CGColorRef colorRef[gradientColors.count];
    for (int i = 0; i < gradientColors.count; i++){
        colorRef[i] = [gradientColors[i] CGColor];
    }
    
    CFArrayRef cfColors = CFArrayCreate(NULL, (void *)colorRef, gradientColors.count, &kCFTypeArrayCallBacks);
    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColors(space, cfColors, NULL);
    
    // Apply gradient
    CGContextClipToMask(context, rect, self.CGImage);
    CGContextDrawLinearGradient(context, gradient, CGPointMake(0,0), CGPointMake(0,self.size.height), 0);
    UIImage *gradientImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return gradientImage;
}

- (UIImage *)imageByApplyingColorMatrix:(const float *)colorMatrix
{
    CGContextRef context = [self createRGBABitmapContext];
    
    CGRect rect = {{0,0},{self.size.width, self.size.height}};
    
    CGContextDrawImage(context, rect, self.CGImage);
    
    unsigned char *imagePixels = CGBitmapContextGetData(context);
    size_t width = CGImageGetWidth(self.CGImage);
    size_t height = CGImageGetHeight(self.CGImage);
    
    size_t rgbIndex = 0;
    
    for(size_t row = 0; row < height; row++) {
        rgbIndex = width * row;
        
        for (size_t column = 0; column < width; column++) {
            uint8_t red     = (uint8_t)imagePixels[rgbIndex];
            uint8_t green   = (uint8_t)imagePixels[rgbIndex+1];
            uint8_t blue    = (uint8_t)imagePixels[rgbIndex+2];
            uint8_t alpha   = (uint8_t)imagePixels[rgbIndex+3];
            
            applyColorMatrix(&red, &green, &blue, &alpha, colorMatrix);
            
            // Assign changed color pixels to image pixels
            imagePixels[rgbIndex] = red;
            imagePixels[rgbIndex+1] = green;
            imagePixels[rgbIndex+2] = blue;
            imagePixels[rgbIndex+3] = alpha;
            
            rgbIndex += 4;
        }
    }
    
    NSInteger dataLength = width * height * 4;
    
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, imagePixels, dataLength, NULL);
    
    size_t bitsPerComponent = 8;
    size_t bitsPerPixel = 32;
    size_t bytesPerRow = 4 * width;
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
    
    CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow,colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);//创建要输出的图像
    
    UIImage *filteredImage = [UIImage imageWithCGImage:imageRef];
    
    CFRelease(imageRef);
    CGColorSpaceRelease(colorSpaceRef);
    CGDataProviderRelease(provider);
    CGContextRelease(context);
    
    return filteredImage;
}

- (CGContextRef)createRGBABitmapContext
{
    CGContextRef context = NULL;
    CGColorSpaceRef colorSpace;
    size_t bitmapByteCount;
    size_t bitmapBytesPerRow;
    
    size_t pixelsWide = CGImageGetWidth(self.CGImage);
    size_t pixelsHigh = CGImageGetHeight(self.CGImage);
    
    bitmapBytesPerRow	= (pixelsWide * 4); //RGBA has 4 bytes
    bitmapByteCount	= (bitmapBytesPerRow * pixelsHigh);
    
    colorSpace = CGColorSpaceCreateDeviceRGB();
    
    context = CGBitmapContextCreate (NULL, pixelsWide, pixelsHigh, 8, bitmapBytesPerRow, colorSpace, 0);
    
    CGColorSpaceRelease( colorSpace );
    
    return context;
}

/* Change RGB by color matrix */
static void applyColorMatrix(uint8_t *red,uint8_t *green,uint8_t *blue,uint8_t *alpha, const float* matrix)
{
    uint8_t redV = *red;
    uint8_t greenV = *green;
    uint8_t blueV = *blue;
    uint8_t alphaV = *alpha;
    
    /* Color matrix algorithm
     
     redResult = (a[0] * srcR) + (a[1] * srcG) + (a[2] * srcB) + (a[3] * srcA) + a[4]

     greenResult = (a[5] * srcR) + (a[6] * srcG) + (a[7] * srcB) + (a[8] * srcA) + a[9]
     
     blueResult = (a[10] * srcR) + (a[11] * srcG) + (a[12] * srcB) + (a[13] * srcA) + a[14]
     
     alphaResult = (a[15] * srcR) + (a[16] * srcG) + (a[17] * srcB) + (a[18] * srcA) + a[19]
     
     */
    
    *red = matrix[0] * redV + matrix[1] * greenV + matrix[2] * blueV + matrix[3] * alphaV + matrix[4];
    *green = matrix[5] * redV + matrix[6] * greenV + matrix[7] * blueV + matrix[8] * alphaV + matrix[9];
    *blue = matrix[10] * redV + matrix[11] * greenV + matrix[12] * blueV + matrix[13] * alphaV + matrix[14];
    *alpha = matrix[15] * redV + matrix[16] * greenV + matrix[17] * blueV + matrix[18] * alphaV + matrix[19];
    
    if (*red > 255) {
        *red = 255;
    }
    
    if(*red < 0) {
        *red = 0;
    }
    
    if (*green > 255) {
        *green = 255;
    }
    
    if (*green < 0) {
        *green = 0;
    }
    
    if (*blue > 255) {
        *blue = 255;
    }
    
    if (*blue < 0) {
        *blue = 0;
    }
    
    if (*alpha > 255) {
        *alpha = 255;
    }
    
    if (*alpha < 0) {
        *alpha = 0;
    }
}

- (UIImage *)scaleToSize:(CGSize)size shouldReserveAspectRatio:(BOOL)shouldReserve scaleMode:(uint8_t)fitOrFill
{
    if (size.width == 0 || size.height == 0) {
        return self;
    }
    
    CGFloat destWidth = size.width;
    CGFloat destHeight = size.height;
    CGFloat srcWidth = self.size.width;
    CGFloat srcHeight = self.size.height;
    
    if (shouldReserve) {
        CGFloat aspectRatio = srcWidth / srcHeight;
        
        if (fitOrFill <= 0) {
            // Fit mode
            if (aspectRatio > 1.0) {
                // Image width is greater than height, we use width to calculate the height, make sure that
                // height does not exceed the specified height.
                destWidth = size.width;
                destHeight = destWidth / aspectRatio;
            } else if (aspectRatio < 1.0){
                // Image width is less than height, we use height to calculate the width, make sure that
                // width does not exceed the specified width.
                destWidth = size.height * aspectRatio;
                destHeight = size.height;
            } else {
                // Image width is equal than height
                destWidth = MIN(size.width, size.height);
                destHeight = destWidth;
            }
        } else {
            // Fill Mode
            if (aspectRatio > 1.0) {
                // Image width is greater than height, we use height to calculate the width, make sure that
                // height can fill the specified height.
                destWidth = size.height * aspectRatio;
                destHeight = size.height;
            } else if (aspectRatio < 1.0){
                // Image width is less than height, we use width to calculate the height, make sure that
                // width can fill the specified width.
                destWidth = size.width;
                destHeight = size.width / aspectRatio;
            } else {
                // Image width is equal than height
                destWidth = MAX(size.width, size.height);
                destHeight = destWidth;
            }
        }
    }
    
    CGRect rect = CGRectMake(0.0, 0.0, destWidth, destHeight);
    UIGraphicsBeginImageContext(rect.size);
    [self drawInRect:rect];
    UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    if (!shouldReserve) {
        return scaledImage;
    }
    
    if (fitOrFill <= 0) {
        // Aspect fit mode, the scaled image is inside the specified rectangle, no need additional process.
        return scaledImage;
    }
    
    // Aspect fill mode, because the image is exceed the bounds of specified rect, we need recenter
    // the image, and crop the outside.
    float x = floorf((destWidth - size.width) / 2 );
    float y = floorf((destHeight - size.height) / 2);
    
    CGRect cropRect = CGRectMake(x ,y , size.width, size.height);
    CGImageRef imageRef = CGImageCreateWithImageInRect(scaledImage.CGImage, cropRect);
    
    if(imageRef == NULL) {
        return scaledImage;
    }
    
    scaledImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    
    return scaledImage;
}

@end
